#include "reactor/acceptor.hpp"
#include "reactor/event_loop.hpp"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>

namespace reactor {

static int createNonblocking() {
    int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
    if (sockfd < 0) {
        std::cerr << "socket() error" << std::endl;
        abort();
    }
    return sockfd;
}

Acceptor::Acceptor(EventLoop* loop, const std::string& ip, uint16_t port)
    : _loop(loop),
      _acceptSocket(createNonblocking()),
      _acceptChannel(loop, _acceptSocket),
      _listening(false)
{
    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);
    servaddr.sin_addr.s_addr = inet_addr(ip.c_str());

    int reuse = 1;
    ::setsockopt(_acceptSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    
    if (::bind(_acceptSocket, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        std::cerr << "bind() error" << std::endl;
        abort();
    }
    _acceptChannel.setReadCallback(std::bind(&Acceptor::handleRead, this));
}

Acceptor::~Acceptor() {
    _acceptChannel.disableAll();
    _acceptChannel.remove();
    ::close(_acceptSocket);
}

void Acceptor::listen() {
    _loop->assertInLoopThread();
    _listening = true;
    if (::listen(_acceptSocket, 1024) < 0) {
        std::cerr << "listen() error" << std::endl;
        abort();
    }
    _acceptChannel.enableReading();
}

void Acceptor::handleRead() {
    _loop->assertInLoopThread();
    struct sockaddr_in peeraddr;
    socklen_t len = sizeof(peeraddr);
    int connfd = ::accept4(_acceptSocket, (struct sockaddr*)&peeraddr, &len, SOCK_NONBLOCK | SOCK_CLOEXEC);
    if (connfd >= 0) {
        if (_newConnectionCallback) {
            _newConnectionCallback(connfd, peeraddr);
        } else {
            ::close(connfd);
        }
    } else {
        std::cerr << "accept4() error" << std::endl;
    }
}

} // namespace reactor
